/*
 * Q.js for Uploader
 * author:devin87@qq.com
 * update:2015/10/23 14:55
 */
(function (window, undefined) {
  "use strict";
  var toString = Object.prototype.toString,
    has = Object.prototype.hasOwnProperty,
    slice = Array.prototype.slice;

  //若value不为undefine,则返回value;否则返回defValue
  function def(value, defValue) {
    return value !== undefined ? value : defValue;
  }
  //检测是否为函数
  function isFunc(fn) {
    //在ie11兼容模式（ie6-8）下存在bug,当调用次数过多时可能返回不正确的结果
    //return typeof fn == "function";
    return toString.call(fn) === "[object Function]";
  }

  //检测是否为正整数
  function isUInt(n) {
    return typeof n == "number" && n > 0 && n === Math.floor(n);
  }

  //触发指定函数,如果函数不存在,则不触发
  function fire(fn, bind) {
    if (isFunc(fn)) return fn.apply(bind, slice.call(arguments, 2));
  }

  //扩展对象
  //forced:是否强制扩展
  function extend(destination, source, forced) {
    if (!destination || !source) return destination;

    for (var key in source) {
      if (key == undefined || !has.call(source, key)) continue;

      if (forced || destination[key] === undefined) destination[key] = source[key];
    }
    return destination;
  }

  //Object.forEach
  extend(Object, {
    //遍历对象
    forEach: function (obj, fn, bind) {
      for (var key in obj) {
        if (has.call(obj, key)) fn.call(bind, key, obj[key], obj);
      }
    }
  });

  extend(Date, {
    //获取当前日期和时间所代表的毫秒数
    now: function () {
      return +new Date;
    }
  });

  //-------------------------- browser ---------------------------
  var browser_ie;

  //ie11 开始不再保持向下兼容(例如,不再支持 ActiveXObject、attachEvent 等特性)
  if (window.ActiveXObject || window.msIndexedDB) {
    //window.ActiveXObject => ie10-
    //window.msIndexedDB   => ie11+

    browser_ie = document.documentMode || (!!window.XMLHttpRequest ? 7 : 6);
  }

  //-------------------------- json ---------------------------

  //json解析
  //secure:是否进行安全检测
  function json_decode(text, secure) {
    //安全检测
    if (secure !== false && !/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) throw new Error("JSON SyntaxError");
    try {
      return (new Function("return " + text))();
    } catch (e) {
    }
  }

  if (!window.JSON) window.JSON = {};
  if (!JSON.parse) JSON.parse = json_decode;

  //-------------------------- DOM ---------------------------

  //设置元素透明
  function setOpacity(ele, value) {
    if (value <= 1) value *= 100;

    if (ele.style.opacity != undefined) ele.style.opacity = value / 100;
    else if (ele.style.filter != undefined) ele.style.filter = "alpha(opacity=" + parseInt(value) + ")";
  }

  //获取元素绝对定位
  function getOffset(ele, root) {
    var left = 0, top = 0, width = ele.offsetWidth, height = ele.offsetHeight;

    do {
      left += ele.offsetLeft;
      top += ele.offsetTop;
      ele = ele.offsetParent;
    } while (ele && ele != root);

    return {left: left, top: top, width: width, height: height};
  }

  //遍历元素节点
  function walk(ele, walk, start, all) {
    var el = ele[start || walk];
    var list = [];
    while (el) {
      if (el.nodeType == 1) {
        if (!all) return el;
        list.push(el);
      }
      el = el[walk];
    }
    return all ? list : null;
  }

  //获取上一个元素节点
  function getPrev(ele) {
    return ele.previousElementSibling || walk(ele, "previousSibling", null, false);
  }

  //获取下一个元素节点
  function getNext(ele) {
    return ele.nextElementSibling || walk(ele, "nextSibling", null, false);
  }

  //获取第一个元素子节点
  function getFirst(ele) {
    return ele.firstElementChild || walk(ele, "nextSibling", "firstChild", false);
  }

  //获取最后一个元素子节点
  function getLast(ele) {
    return ele.lastElementChild || walk(ele, "previousSibling", "lastChild", false);
  }

  //获取所有子元素节点
  function getChilds(ele) {
    return ele.children || walk(ele, "nextSibling", "firstChild", true);
  }

  //创建元素
  function createEle(tagName, className, html) {
    var ele = document.createElement(tagName);
    if (className) ele.className = className;
    if (html) ele.innerHTML = html;

    return ele;
  }

  //解析html标签
  function parseHTML(html, all) {
    var box = createEle("div", "", html);
    return all ? box.childNodes : getFirst(box);
  }

  //-------------------------- event ---------------------------

  var addEvent,
    removeEvent;

  if (document.addEventListener) {  //w3c
    addEvent = function (ele, type, fn) {
      ele.addEventListener(type, fn, false);
    };

    removeEvent = function (ele, type, fn) {
      ele.removeEventListener(type, fn, false);
    };
  } else if (document.attachEvent) {  //IE
    addEvent = function (ele, type, fn) {
      ele.attachEvent("on" + type, fn);
    };

    removeEvent = function (ele, type, fn) {
      ele.detachEvent("on" + type, fn);
    };
  }

  //event简单处理
  function fix_event(event) {
    var e = event || window.event;

    //for ie
    if (!e.target) e.target = e.srcElement;

    return e;
  }

  //添加事件
  function add_event(element, type, handler, once) {
    var fn = function (e) {
      handler.call(element, fix_event(e));

      if (once) removeEvent(element, type, fn);
    };

    addEvent(element, type, fn);

    if (!once) {
      return {
        //直接返回停止句柄 eg:var api=add_event();api.stop();
        stop: function () {
          removeEvent(element, type, fn);
        }
      };
    }
  }

  //触发事件
  function trigger_event(ele, type) {
    if (isFunc(ele[type])) ele[type]();
    else if (ele.fireEvent) ele.fireEvent("on" + type);  //ie10-
    else if (ele.dispatchEvent) {
      var evt = document.createEvent("HTMLEvents");

      //initEvent接受3个参数:事件类型,是否冒泡,是否阻止浏览器的默认行为
      evt.initEvent(type, true, true);

      //鼠标事件,设置更多参数
      //var evt = document.createEvent("MouseEvents");
      //evt.initMouseEvent(type, true, true, ele.ownerDocument.defaultView, 1, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);

      ele.dispatchEvent(evt);
    }
  }

  //阻止事件默认行为并停止事件冒泡
  function stop_event(event, isPreventDefault, isStopPropagation) {
    var e = fix_event(event);

    //阻止事件默认行为
    if (isPreventDefault !== false) {
      if (e.preventDefault) e.preventDefault();
      else e.returnValue = false;
    }

    //停止事件冒泡
    if (isStopPropagation !== false) {
      if (e.stopPropagation) e.stopPropagation();
      else e.cancelBubble = true;
    }
  }

  //---------------------- other ----------------------

  var RE_HTTP = /^https?:\/\//i;

  //是否http路径(以 http:// 或 https:// 开头)
  function isHttpURL(url) {
    return RE_HTTP.test(url);
  }

  //判断指定路径与当前页面是否同域(包括协议检测 eg:http与https不同域)
  function isSameHost(url) {
    if (!isHttpURL(url)) return true;

    var start = RegExp.lastMatch.length,
      end = url.indexOf("/", start),
      host = url.slice(0, end != -1 ? end : undefined);

    return host.toLowerCase() == (location.protocol + "//" + location.host).toLowerCase();
  }

  //按照进制解析数字的层级 eg:时间转化 -> parseLevel(86400,[60,60,24]) => { value=1, level=3 }
  //steps:步进,可以是固定的数字(eg:1024),也可以是具有层次关系的数组(eg:[60,60,24])
  //limit:限制解析的层级,正整数,默认为100
  function parseLevel(size, steps, limit) {
    size = +size;
    steps = steps || 1024;

    var level = 0,
      isNum = typeof steps == "number",
      stepNow = 1,
      count = isUInt(limit) ? limit : (isNum ? 100 : steps.length);

    while (size >= stepNow && level < count) {
      stepNow *= (isNum ? steps : steps[level]);
      level++;
    }

    if (level && size < stepNow) {
      stepNow /= (isNum ? steps : steps.last());
      level--;
    }

    return {value: level ? size / stepNow : size, level: level};
  }

  var UNITS_FILE_SIZE = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];

  //格式化数字输出,将数字转为合适的单位输出,默认按照1024层级转为文件单位输出
  function formatSize(size, ops) {
    ops = ops === true ? {all: true} : ops || {};

    if (isNaN(size) || size == undefined || size < 0) {
      var error = ops.error || "--";

      return ops.all ? {text: error} : error;
    }

    var pl = parseLevel(size, ops.steps, ops.limit),

      value = pl.value,
      text = value.toFixed(def(ops.digit, 2));

    if (ops.trim !== false && text.lastIndexOf(".") != -1) text = text.replace(/\.?0+$/, "");

    pl.text = text + (ops.join || "") + (ops.units || UNITS_FILE_SIZE)[pl.level + (ops.start || 0)];

    return ops.all ? pl : pl.text;
  }

  //---------------------- export ----------------------

  var Q = {
    def: def,
    isFunc: isFunc,
    isUInt: isUInt,

    fire: fire,
    extend: extend,

    ie: browser_ie,

    setOpacity: setOpacity,
    getOffset: getOffset,

    walk: walk,
    getPrev: getPrev,
    getNext: getNext,
    getFirst: getFirst,
    getLast: getLast,
    getChilds: getChilds,

    createEle: createEle,
    parseHTML: parseHTML,

    isHttpURL: isHttpURL,
    isSameHost: isSameHost,

    parseLevel: parseLevel,
    formatSize: formatSize
  };

  if (browser_ie) Q["ie" + (browser_ie < 6 ? 6 : browser_ie)] = true;

  Q.event = {
    fix: fix_event,
    stop: stop_event,
    trigger: trigger_event,

    add: add_event
  };

  window.Q = Q;

})(window);


/// <reference path="Q.js" />
/// <reference path="Q.md5File.js" />
/*
 * Q.Uploader.js 文件上传管理器 1.0
 * https://github.com/devin87/web-uploader
 * author:devin87@qq.com
 * update:2017/02/09 09:03
 */
(function (window, undefined) {
  "use strict";

  var def = Q.def,
    fire = Q.fire,
    extend = Q.extend,

    getFirst = Q.getFirst,
    getLast = Q.getLast,

    parseJSON = JSON.parse,

    createEle = Q.createEle,
    parseHTML = Q.parseHTML,

    setOpacity = Q.setOpacity,
    getOffset = Q.getOffset,

    md5File = Q.md5File,

    E = Q.event,
    addEvent = E.add,
    triggerEvent = E.trigger,
    stopEvent = E.stop;

  //Object.forEach
  //Date.now

  //-------------------------------- Uploader --------------------------------

  var support_html5_upload = false,        //是否支持html5(ajax)方式上传
    support_multiple_select = false,     //是否支持文件多选

    support_file_click_trigger = false,  //上传控件是否支持click触发文件选择 eg: input.click() => ie9及以下不支持

    UPLOADER_GUID = 0,                   //文件上传管理器唯一标示,多用于同一个页面存在多个管理器的情况

    UPLOAD_TASK_GUID = 0,                //上传任务唯一标示
    UPLOAD_HTML4_ZINDEX = 0;             //防止多个上传管理器的触发按钮位置重复引起的问题

  //上传状态
  var UPLOAD_STATE_READY = 0,              //任务已添加,准备上传
    UPLOAD_STATE_PROCESSING = 1,         //任务上传中
    UPLOAD_STATE_COMPLETE = 2,           //任务上传完成

    UPLOAD_STATE_SKIP = -1,              //任务已跳过(不会上传)
    UPLOAD_STATE_CANCEL = -2,            //任务已取消
    UPLOAD_STATE_ERROR = -3;             //任务已失败

  //获取上传状态说明
  function get_upload_status_text(state) {
    var LANG = Uploader.Lang;

    switch (state) {
      case UPLOAD_STATE_READY:
        return LANG.status_ready;
      case UPLOAD_STATE_PROCESSING:
        return LANG.status_processing;
      case UPLOAD_STATE_COMPLETE:
        return LANG.status_complete;

      case UPLOAD_STATE_SKIP:
        return LANG.status_skip;
      case UPLOAD_STATE_CANCEL:
        return LANG.status_cancel;
      case UPLOAD_STATE_ERROR:
        return LANG.status_error;
    }

    return state;
  }

  //上传探测
  function detect() {
    var XHR = window.XMLHttpRequest;
    if (XHR && new XHR().upload && window.FormData) support_html5_upload = true;

    var input = document.createElement("input");
    input.type = "file";

    support_multiple_select = !!input.files;
    support_file_click_trigger = support_html5_upload;
  }

  //截取字符串
  function get_last_find(str, find) {
    var index = str.lastIndexOf(find);
    return index != -1 ? str.slice(index) : "";
  }

  //将逗号分隔的字符串转为键值对
  function split_to_map(str) {
    if (!str) return;

    var list = str.split(","), map = {};

    for (var i = 0, len = list.length; i < len; i++) {
      map[list[i]] = true;
    }

    return map;
  }

  //iframe load 事件
  //注意：低版本 ie 支持 iframe 的 onload 事件,不过是隐形的(iframe.onload 方式绑定的将不会触发),需要通过 attachEvent 来注册
  function bind_iframe_load(iframe, fn) {
    if (iframe.attachEvent) iframe.attachEvent("onload", fn);
    else iframe.addEventListener("load", fn, false);
  }

  //计算上传速度
  function set_task_speed(task, total, loaded) {
    if (!total || total <= 0) return;

    var nowTime = Date.now(), tick;

    //上传完毕,计算平均速度(Byte/s)
    if (loaded >= total) {
      tick = nowTime - task.startTime;
      if (tick) task.avgSpeed = Math.min(Math.round(total * 1000 / tick), total);
      else if (!task.speed) task.avgSpeed = task.speed = total;

      task.time = tick || 0;
      task.endTime = nowTime;
      return;
    }

    //即时速度(Byte/s)
    tick = nowTime - task.lastTime;
    if (tick < 200) return;

    task.speed = Math.min(Math.round((loaded - task.loaded) * 1000 / tick), task.total);
    task.lastTime = nowTime;
  }

  /*
   文件上传管理器,调用示例
   new Uploader({
   //--------------- 必填 ---------------
   url: "",            //上传路径
   target: element,    //上传按钮
   view: element,      //上传任务视图(需加载UI接口默认实现)

   //--------------- 可选 ---------------
   html5: true,       //是否启用html5上传,若浏览器不支持,则自动禁用
   multiple: true,    //选择文件时是否允许多选,若浏览器不支持,则自动禁用(仅html5模式有效)

   clickTrigger:true, //是否启用click触发文件选择 eg: input.click() => ie9及以下不支持

   auto: true,        //添加任务后是否立即上传,false不建议手动上传

   data: {},          //上传文件的同时可以指定其它参数,该参数将以POST的方式提交到服务器

   workerThread: 1,   //同时允许上传的任务数(仅html5模式有效)

   upName: "upfile",  //上传参数名称,若后台需要根据name来获取上传数据,可配置此项

   allows: "",        //允许上传的文件类型(扩展名),多个之间用逗号隔开
   disallows: "",     //禁止上传的文件类型(扩展名)

   isSlice: false,               //是否启用分片上传，若为true，则isQueryState和isMd5默认为true
   chunkSize: 2 * 1024 * 1024,   //默认分片大小为2MB
   isQueryState:false,           //是否查询文件状态（for 秒传或续传）
   isMd5: false,                 //是否计算上传文件md5值
   isUploadAfterHash:true,       //是否在Hash计算完毕后再上传

   container:element, //一般无需指定
   getPos:function,   //一般无需指定

   //上传回调事件(function)
   on: {
   init,          //上传管理器初始化完毕后触发

   select,        //点击上传按钮准备选择上传文件之前触发,返回false可禁止选择文件
   add[Async],    //添加任务之前触发,返回false将跳过该任务
   upload[Async], //上传任务之前触发,返回false将跳过该任务
   send[Async],   //发送数据之前触发,返回false将跳过该任务

   cancel,        //取消上传任务后触发
   remove,        //移除上传任务后触发

   progress,      //上传进度发生变化后触发(仅html5模式有效)
   complete       //上传完成后触发
   },

   //UI接口(function),若指定了以下方法,将忽略默认实现
   UI:{
   init,       //执行初始化操作
   draw,       //添加任务后绘制任务界面
   update,     //更新任务界面
   over        //任务上传完成
   }
   });
   */
  function Uploader(ops) {
    var self = this;

    //---------------- settings ----------------

    self.guid = ops.guid || "uploader" + (++UPLOADER_GUID);

    self.url = ops.url;                      //上传路径
    self.dataType = ops.dataType || "json";  //返回值类型
    self.data = ops.data;                    //上传参数
    self.target = ops.target;                //上传按钮

    //是否以html5(ajax)方式上传
    self.html5 = support_html5_upload && !!def(ops.html5, true);

    //是否允许多选(仅在启用了html5的情形下生效)
    //在html4模式下,input是一个整体,若启用多选,将无法针对单一的文件进行操作(eg:根据扩展名筛选、取消、删除操作等)
    //若无需对文件进行操作,可通过 uploader.multiple = true 强制启用多选(不推荐)
    self.multiple = support_multiple_select && self.html5 && !!def(ops.multiple, true);

    //是否启用click触发文件选择 eg: input.click() => ie9及以下不支持
    self.clickTrigger = support_file_click_trigger && !!def(ops.clickTrigger, true);

    //允许同时上传的数量(html5有效)
    //由于设计原因,html4仅能同时上传1个任务,请不要更改
    self.workerThread = self.html5 ? ops.workerThread || 1 : 1;
    self.workerIdle = self.workerThread;

    self.auto = ops.auto !== false;                 //是否在添加任务后自动开始

    self.upName = ops.upName || "upfile";           //上传参数名

    self.allows = split_to_map(ops.allows);         //允许上传的文件类型（扩展名）,多个之间用逗号隔开 eg:.jpg,.png
    self.disallows = split_to_map(ops.disallows);   //禁止上传的文件类型（扩展名）,多个之间用逗号隔开

    self.chunkSize = ops.chunkSize || 2 * 1024 * 1024;            //分片上传大小
    self.isSlice = !!ops.isSlice;                                 //是否启用分片上传
    self.isQueryState = !!def(ops.isQueryState, self.isSlice);    //是否查询文件状态（for 秒传或续传）
    self.isMd5 = !!def(ops.isMd5, self.isSlice);                  //是否计算上传文件md5值
    self.isUploadAfterHash = ops.isUploadAfterHash !== false;     //是否在Hash计算完毕后再上传

    //ie9及以下不支持click触发(即使能弹出文件选择框,也无法获取文件数据,报拒绝访问错误)
    //若上传按钮位置不确定(比如在滚动区域内),则无法触发文件选择
    //设置原则:getPos需返回上传按钮距container的坐标
    self.container = ops.container || document.body;
    //函数,获取上传按钮距container的坐标,返回格式 eg:{ left: 100, top: 100 }
    if (ops.getPos) self.getPos = ops.getPos;

    //UI接口,此处将覆盖 prototype 实现
    var UI = ops.UI || {};
    if (UI.init) self.init = UI.init;             //执行初始化操作
    if (UI.draw) self.draw = UI.draw;             //添加任务后绘制任务界面
    if (UI.update) self.update = UI.update;       //更新任务界面
    if (UI.over) self.over = UI.over;             //任务上传完成

    //上传回调事件
    self.fns = ops.on || {};

    //上传选项
    self.ops = ops;

    //---------------- init ----------------

    self.list = [];
    self.map = {};

    self.index = 0;
    self.started = false;

    self._init();
  }

  Uploader.prototype = {
    //修复constructor指向
    constructor: Uploader,

    //初始化上传管理器
    _init: function () {
      var self = this;

      if (self._inited) return;
      self._inited = true;

      var guid = self.guid,
        target = self.target,
        container = self.container;

      var boxInput = createEle("div", "upload-input " + guid);
      container.appendChild(boxInput);

      self.boxInput = boxInput;

      //构造html4上传所需的iframe和form
      if (!self.html5) {
        var iframe_name = "upload_iframe_" + guid;

        var html =
          '<iframe class="u-iframe" name="' + iframe_name + '"></iframe>' +
          '<form class="u-form" action="" method="post" enctype="multipart/form-data" target="' + iframe_name + '"></form>';

        var boxHtml4 = createEle("div", "upload-html4 " + guid, html);
        container.appendChild(boxHtml4);

        var iframe = getFirst(boxHtml4),
          form = getLast(boxHtml4);

        self.iframe = iframe;
        self.form = form;

        //html4上传完成回调
        bind_iframe_load(iframe, function () {
          if (self.workerIdle != 0) return;

          var text;
          try {
            text = iframe.contentWindow.document.body.innerHTML;
          } catch (e) {
          }

          self.complete(undefined, UPLOAD_STATE_COMPLETE, text);
        });
      }

      if (self.clickTrigger) {
        addEvent(target, "click", function (e) {
          if (self.fire("select", e) === false) return;

          self.resetInput();

          //注意:ie9及以下可以弹出文件选择框,但获取不到选择数据,拒绝访问。
          triggerEvent(self.inputFile, "click");
        });
      } else {
        addEvent(boxInput, "click", function (e) {
          if (self.fire("select", e) === false) stopEvent(e);
        });

        setOpacity(boxInput, 0);

        self.resetInput();
      }

      self.fire("init");

      return self.run("init");
    },

    //重置上传控件
    resetInput: function () {
      var self = this,

        boxInput = self.boxInput;

      boxInput.innerHTML = '<input type="file" name="' + self.upName + '" style="' + (self.clickTrigger ? 'visibility: hidden;' : 'font-size:100px;') + '"' + (self.multiple ? ' multiple="multiple"' : '') + '>';

      var inputFile = getFirst(boxInput);

      //文件选择事件
      addEvent(inputFile, "change", function (e) {
        self.add(this);

        //html4 重置上传控件
        if (!self.html5) self.resetInput();
      });

      self.inputFile = inputFile;

      return self.updatePos();
    },
    //更新上传按钮坐标(for ie)
    updatePos: function (has_more_uploader) {
      var self = this;
      if (self.clickTrigger) return self;

      var getPos = self.getPos || getOffset,

        boxInput = self.boxInput,
        inputFile = getFirst(boxInput),
        target = self.target,

        inputWidth = target.offsetWidth,
        inputHeight = target.offsetHeight,

        pos = inputWidth == 0 ? {left: -10000, top: -10000} : getPos(target);

      boxInput.style.width = inputFile.style.width = inputWidth + "px";
      boxInput.style.height = inputFile.style.height = inputHeight + "px";

      boxInput.style.left = pos.left + "px";
      boxInput.style.top = pos.top + "px";

      //多用于选项卡切换中上传按钮位置重复的情况
      if (has_more_uploader) boxInput.style.zIndex = ++UPLOAD_HTML4_ZINDEX;

      return self;
    },
    //触发ops上定义的回调方法,优先触发异步回调(以Async结尾)
    fire: function (action, arg, callback) {
      if (!callback) return fire(this.fns[action], this, arg);

      var asyncFun = this.fns[action + "Async"];
      if (asyncFun) return fire(asyncFun, this, arg, callback);

      callback(fire(this.fns[action], this, arg));
    },

    //运行内部方法或扩展方法(如果存在)
    run: function (action, arg) {
      var fn = this[action];
      if (fn) fire(fn, this, arg);
      return this;
    },

    //添加一个上传任务
    addTask: function (input, file) {
      if (!input && !file) return;

      var name, size;

      if (file) {
        name = file.name || file.fileName;
        size = file.size || file.fileSize;
      } else {
        name = get_last_find(input.value, "\\").slice(1) || input.value;
        size = -1;
      }

      var self = this,

        ext = get_last_find(name, ".").toLowerCase(),
        isSkip = (self.disallows && self.disallows[ext]) || (self.allows && !self.allows[ext]);

      var task = {
        id: ++UPLOAD_TASK_GUID,

        name: name,
        ext: ext,
        size: size,

        input: input,
        file: file,

        state: isSkip ? UPLOAD_STATE_SKIP : UPLOAD_STATE_READY
      };

      if (isSkip) task.disabled = true;

      self.fire("add", task, function (result) {
        if (result === false || task.disabled) return;

        task.index = self.list.length;
        self.list.push(task);
        self.map[task.id] = task;

        self.run("draw", task);

        if (self.auto) self.start();
      });

      return task;
    },

    //添加上传任务,自动判断input(是否多选)或file
    add: function (input_or_file) {
      var self = this;

      if (input_or_file.tagName == "INPUT") {
        var files = input_or_file.files;
        if (files) {
          for (var i = 0, len = files.length; i < len; i++) {
            self.addTask(input_or_file, files[i]);
          }
        } else {
          self.addTask(input_or_file);
        }
      } else {
        self.addTask(undefined, input_or_file);
      }
    },

    //批量添加上传任务
    addList: function (list) {
      for (var i = 0, len = list.length; i < len; i++) {
        this.add(list[i]);
      }
    },

    //获取指定任务
    get: function (taskId) {
      if (taskId != undefined) return this.map[taskId];
    },

    //取消上传任务
    //onlyCancel: 若为true,则仅取消上传而不触发任务完成事件
    cancel: function (taskId, onlyCancel) {
      var self = this,
        task = self.get(taskId);

      if (!task) return;

      var state = task.state;

      //若任务已完成,直接返回
      if (state != UPLOAD_STATE_READY && state != UPLOAD_STATE_PROCESSING) return self;

      if (state == UPLOAD_STATE_PROCESSING) {
        //html5
        var xhr = task.xhr;
        if (xhr) {
          xhr.abort();

          //无需调用complete,html5 有自己的处理,此处直接返回
          return self;
        }

        //html4
        self.iframe.contentWindow.location = "about:blank";
      }

      return onlyCancel ? self : self.complete(task, UPLOAD_STATE_CANCEL);
    },

    //移除任务
    remove: function (taskId) {
      var task = this.get(taskId);
      if (!task) return;

      if (task.state == UPLOAD_STATE_PROCESSING) this.cancel(taskId);

      //this.list.splice(task.index, 1);
      //this.map[task.id] = undefined;

      //从数组中移除任务时,由于任务是根据index获取,若不处理index,将导致上传错乱甚至不能上传
      //此处重置上传索引,上传时会自动修正为正确的索引(程序会跳过已处理过的任务)
      //this.index = 0;

      //添加移除标记(用户可以自行操作,更灵活)
      task.deleted = true;

      this.fire("remove", task);
    },

    //开始上传
    start: function () {
      var self = this,

        workerIdle = self.workerIdle,

        list = self.list,
        index = self.index,

        count = list.length;

      if (!self.started) self.started = true;

      if (count <= 0 || index >= count || workerIdle <= 0) return self;

      var task = list[index];
      self.index++;

      return self.upload(task);
    },

    //上传任务
    upload: function (task) {
      var self = this;

      if (!task || task.state != UPLOAD_STATE_READY || task.skip) return self.start();

      task.url = self.url;
      self.workerIdle--;

      self.fire("upload", task, function (result) {
        if (result === false) return self.complete(task, UPLOAD_STATE_SKIP);

        if (self.html5 && task.file) self._upload_html5_ready(task);
        else if (task.input) self._upload_html4(task);
        else self.complete(task, UPLOAD_STATE_SKIP);
      });

      return self;
    },

    //根据 task.hash 查询任务状态（for 秒传或续传）
    queryState: function (task, callback) {
      var self = this,
        url = self.url,
        xhr = new XMLHttpRequest();

      task.queryUrl = url + (url.indexOf("?") == -1 ? "?" : "&") + "action=query&hash=" + (task.hash || task.name);

      //秒传查询事件
      self.fire("sliceQuery", task);

      xhr.open("GET", task.queryUrl);
      //xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

      xhr.onreadystatechange = function () {
        if (xhr.readyState != 4) return;

        var responseText, json;

        if (xhr.status >= 200 && xhr.status < 400) {
          responseText = xhr.responseText;

          if (responseText === "ok") json = {ret: 1};
          else if (responseText) json = parseJSON(responseText);

          if (!json || typeof json == "number") json = {ret: 0, start: json};

          task.response = responseText;
          task.json = json;

          if (json.ret == 1) {
            task.queryOK = true;
            self.cancel(task.id, true).complete(task, UPLOAD_STATE_COMPLETE);
          } else {
            var start = +json.start || 0;
            if (start != Math.floor(start)) start = 0;

            task.sliceStart = start;
          }
        }

        fire(callback, self, xhr);
      };

      xhr.onerror = function () {
        fire(callback, self, xhr);
      };

      xhr.send(null);

      return self;
    },

    //处理html5上传（包括秒传和断点续传）
    _upload_html5_ready: function (task) {
      var self = this;

      //上传处理
      var goto_upload = function () {
        if (task.state == UPLOAD_STATE_COMPLETE) return;

        if (self.isSlice) self._upload_slice(task);
        else self._upload_html5(task);
      };

      var after_hash = function (callback) {
        //自定义hash事件
        self.fire("hash", task, function () {
          if (task.hash && self.isQueryState && task.state != UPLOAD_STATE_COMPLETE) self.queryState(task, callback);
          else callback();
        });
      };

      //计算文件hash
      var compute_hash = function (callback) {
        //计算上传文件md5值
        if (self.isMd5 && md5File) {
          var hashProgress = self.fns.hashProgress;

          md5File(task.file, function (md5, time) {
            task.hash = md5;
            task.timeHash = time;
            after_hash(callback);
          }, function (pvg) {
            fire(hashProgress, self, task, pvg);
          });
        } else {
          after_hash(callback);
        }
      };

      if (self.isUploadAfterHash) {
        compute_hash(goto_upload);
      } else {
        goto_upload();
        compute_hash();
      }

      return self;
    },

    //处理上传参数
    _process_params: function (task, fn) {
      if (this.data) Object.forEach(this.data, fn);
      if (task.data) Object.forEach(task.data, fn);
    },

    //以html5的方式上传任务
    _upload_html5: function (task) {
      var self = this,
        xhr = new XMLHttpRequest();

      task.xhr = xhr;

      xhr.upload.addEventListener("progress", function (e) {
        self.progress(task, e.total, e.loaded);
      }, false);

      xhr.addEventListener("load", function (e) {
        self.complete(task, UPLOAD_STATE_COMPLETE, e.target.responseText);
      }, false);

      xhr.addEventListener("error", function () {
        self.complete(task, UPLOAD_STATE_ERROR);
      }, false);

      xhr.addEventListener("abort", function () {
        self.complete(task, UPLOAD_STATE_CANCEL);
      }, false);

      var fd = new FormData;

      //处理上传参数
      self._process_params(task, function (k, v) {
        fd.append(k, v);
      });

      fd.append("fileName", task.name);
      fd.append(self.upName, task.blob || task.file, task.name);

      xhr.open("POST", task.url);

      //移除自定义标头,以防止跨域上传被拦截
      //xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

      self.fire("send", task, function (result) {
        if (result === false) return self.complete(task, UPLOAD_STATE_SKIP);

        xhr.send(fd);

        self._afterSend(task);
      });
    },

    //以传统方式上传任务
    _upload_html4: function (task) {
      var self = this,
        form = self.form,
        input = task.input;

      //解决多选的情况下重复上传的问题(即使如此，仍然不建议html4模式下开启多选)
      if (input._uploaded) return self.complete(task, UPLOAD_STATE_COMPLETE);
      input._uploaded = true;

      input.name = self.upName;

      form.innerHTML = "";
      form.appendChild(input);

      form.action = task.url;

      //处理上传参数
      self._process_params(task, function (k, v) {
        form.appendChild(parseHTML('<input type="hidden" name="' + k + '" value="' + v + '">'));
      });

      self.fire("send", task, function (result) {
        if (result === false) return self.complete(task, UPLOAD_STATE_SKIP);

        form.submit();

        self._afterSend(task);
      });
    },

    //已开始发送数据
    _afterSend: function (task) {
      task.lastTime = task.startTime = Date.now();

      task.state = UPLOAD_STATE_PROCESSING;
      this._lastTask = task;

      this.progress(task);
    },

    //更新进度显示
    progress: function (task, total, loaded) {
      if (!total) total = task.size;
      if (!loaded || loaded < 0) loaded = 0;

      var state = task.state || UPLOAD_STATE_READY;

      if (loaded > total) loaded = total;
      if (loaded > 0 && state == UPLOAD_STATE_READY) task.state = state = UPLOAD_STATE_PROCESSING;

      var completed = state == UPLOAD_STATE_COMPLETE;
      if (completed) total = loaded = task.size;

      //计算上传速度
      set_task_speed(task, total, loaded);

      task.total = total;
      task.loaded = loaded;

      this.fire("progress", task);
      this.run("update", task);
    },

    //处理响应数据
    _process_response: function (task, responseText) {
      task.response = responseText;
      if (!responseText) return;

      if (this.dataType == "json") task.json = parseJSON(responseText);
    },

    //完成上传
    complete: function (task, state, responseText) {
      var self = this;

      if (!task && self.workerThread == 1) task = self._lastTask;

      if (task) {
        if (state != undefined) task.state = state;

        if (task.state == UPLOAD_STATE_PROCESSING || state == UPLOAD_STATE_COMPLETE) {
          task.state = UPLOAD_STATE_COMPLETE;
          self.progress(task, task.size, task.size);
        }

        if (responseText !== undefined) self._process_response(task, responseText);
      }

      if (state == UPLOAD_STATE_CANCEL) self.fire("cancel", task);
      self.fire("complete", task);

      self.run("over", task).run("update", task);

      self.workerIdle++;
      if (self.started) self.start();

      return self;
    }
  };

  //扩展上传管理器
  //forced:是否强制覆盖
  Uploader.extend = function (source, forced) {
    extend(Uploader.prototype, source, forced);
  };

  //---------------------- export ----------------------
  detect();

  extend(Uploader, {
    support: {
      html5: support_html5_upload,
      multiple: support_multiple_select
    },

    READY: UPLOAD_STATE_READY,
    PROCESSING: UPLOAD_STATE_PROCESSING,
    COMPLETE: UPLOAD_STATE_COMPLETE,

    SKIP: UPLOAD_STATE_SKIP,
    CANCEL: UPLOAD_STATE_CANCEL,
    ERROR: UPLOAD_STATE_ERROR,

    //默认语言
    Lang: {
      status_ready: "准备中",
      status_processing: "上传中",
      status_complete: "已完成",
      status_skip: "已跳过",
      status_cancel: "已取消",
      status_error: "已失败"
    },

    getStatusText: get_upload_status_text
  });

  Q.Uploader = Uploader;

})(window);

/// <reference path="Q.Uploader.js" />
/*1
 * Q.Uploader.Image.js 图片上传管理器界面
 * author:devin87@qq.com
 * update:2016/04/22 16:07
 */
(function (window, undefined) {
  "use strict";

  var getFirst = Q.getFirst,
    //getLast = Q.getLast,
    getNext = Q.getNext,

    createEle = Q.createEle,
    //formatSize = Q.formatSize,
    setOpacity = Q.setOpacity,

    browser_ie = Q.ie,

    Uploader = Q.Uploader;

  //追加css样式名
  function addClass(ele, className) {
    ele.className += " " + className;
  }

  //设置元素内容
  function setHtml(ele, html) {
    if (ele) ele.innerHTML = html || "";
  }

  //生成图片预览地址(html5)
  function readAsURL(file, callback) {
    var URL = window.URL || window.webkitURL;
    if (URL) return callback(URL.createObjectURL(file));

    if (window.FileReader) {
      var fr = new FileReader();
      fr.onload = function (e) {
        callback(e.target.result);
      };
      fr.readAsDataURL(file);
    } else if (file.readAsDataURL) {
      callback(file.readAsDataURL());
    }
  }

  //图片预览
  function previewImage(box, task, callback) {
    var input = task.input,
      file = task.file || (input.files ? input.files[0] : undefined);

    if (file) {
      //IE10+、Webkit、Firefox etc
      readAsURL(file, function (src) {
        if (src) box.innerHTML = '<img src="' + src + '" />';

        callback && callback(src);
      });
    } else if (input) {
      var src = input.value;

      if (!src || /^\w:\\fakepath/.test(src)) {
        input.select();
        //解决ie报拒绝访问的问题
        parent.document.body.focus();
        //获取图片真实地址
        if (document.selection) src = document.selection.createRange().text;
      }

      if (src) {
        box.innerHTML = '<img src="' + src + '" />';

        try {
          if (browser_ie > 6) box.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + src + "')";
        } catch (e) {
        }
      }

      callback && callback(src);
    }
  }

  var Blob = window.Blob || window.WebkitBlob || window.MozBlob,
    BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder;

  //是否支持图片缩放
  var support_image_scale = (function () {
    if (!window.FileReader || !window.atob || !(Blob || BlobBuilder)) return false;

    var canvas = document.createElement("canvas");
    return !!canvas.getContext && !!canvas.getContext("2d");
  })();

  //获取mimetype
  function get_image_mimetype(ext) {
    var type = ext.slice(1);
    return "image/" + (type == "jpg" ? "jpeg" : type);
  }

  //将dataURL转为Blob对象，以用于ajax上传
  function dataURLtoBlob(base64, mimetype) {
    var ds = base64.split(','),
      data = atob(ds[1]),

      arr = [];

    for (var i = 0, len = data.length; i < len; i++) {
      arr[i] = data.charCodeAt(i);
    }

    if (Blob) return new Blob([new Uint8Array(arr)], {type: mimetype});

    var builder = new BlobBuilder();
    builder.append(arr);
    return builder.getBlob(mimetype);
  }

  //图片缩放
  function scaleImage(src, mimetype, ops, callback) {
    var image = new Image();
    image.src = src;

    image.onload = function () {
      var width = image.width,
        height = image.height,

        maxWidth = ops.maxWidth,
        maxHeight = ops.maxHeight,

        hasWidthScale = maxWidth && width > maxWidth,
        hasHeightScale = maxHeight && height > maxHeight,

        hasScale = hasWidthScale || hasHeightScale;

      //无需压缩
      if (!hasScale) return callback && callback(false);

      //根据宽度缩放
      if (hasWidthScale) {
        width = maxWidth;
        height = Math.floor(image.height * width / image.width);
      }

      //根据高度缩放
      if (hasHeightScale) {
        height = maxHeight;
        width = Math.floor(image.width * height / image.height);
      }

      var canvas = document.createElement("canvas"),
        ctx = canvas.getContext("2d");

      canvas.width = width;
      canvas.height = height;

      ctx.drawImage(image, 0, 0, width, height);

      callback && callback(canvas.toDataURL(mimetype), mimetype);
    };
  }

  //默认图片格式
  var DEF_IMAGE_TYPES = ".jpg,.png,.gif,.bmp,.webp,.tif,.tiff",
    //默认缩放的图片类型
    DEF_IMAGE_SCALE_TYPES = ".jpg";

  //是否支持图片缩放
  Uploader.support.imageScale = support_image_scale;

  Uploader.previewImage = previewImage;
  Uploader.scaleImage = scaleImage;


  //实现默认的UI接口
  Uploader.extend({
    //初始化
    init: function () {
      var ops = this.ops,
        boxView = ops.boxView;

      if (!ops.allows) ops.allows = DEF_IMAGE_TYPES;

      if (boxView) addClass(boxView, this.html5 ? "html5" : "html4");
    },

    //是否支持图片压缩
    supportScale: function (type) {
      if (!support_image_scale) return false;

      if (type == ".jpeg") type = ".jpg";

      var types = (this.ops.scale || {}).types || DEF_IMAGE_SCALE_TYPES,
        isImage = DEF_IMAGE_TYPES.indexOf(type) != -1;

      if (!isImage) return false;

      return types == "*" || types.indexOf(type) != -1;
    },

    //预览并压缩图片
    previewImage: function (boxImage, task, ops) {
      var self = this,
        scale_data = task.scale || ops.scale,
        support_scale = scale_data && self.supportScale(task.ext);

      if (support_scale) task.skip = true;

      previewImage(boxImage, task, function (src) {
        self.fire("preview", {task: task, src: src});

        if (!src || !support_scale) return;

        scaleImage(src, get_image_mimetype(task.ext), scale_data, function (base64, mimetype) {
          if (base64) {
            var blob = dataURLtoBlob(base64, mimetype);
            task.blob = blob;

            self.fire("scale", {task: task, base64: base64, type: mimetype, blob: blob});
          }

          task.skip = false;
          self.list.push(task);

          if (self.auto) self.start();
        });
      });

      return self;
    },

    //绘制任务UI
    draw: function (task) {
      var self = this,
        ops = self.ops,
        boxView = ops.view;

      if (!boxView) return;

      var name = task.name;

      var html =
        '<div class="u-img"></div>' +
        '<div class="u-progress-bar">' +
        '<div class="u-progress"></div>' +
        '</div>' +
        '<div class="u-detail"></div>';
      // '<div class="u-name" title="' + name + '">' + name + '</div>';

      var taskId = task.id,
        box = createEle("div", "u-item", html);

      box.taskId = taskId;

      var boxImage = getFirst(box),
        boxProgressbar = getNext(boxImage),
        boxProgress = getFirst(boxProgressbar),
        boxDetail = getNext(boxProgressbar);

      setOpacity(boxProgressbar, 0.3);
      setOpacity(boxProgress, 0.5);

      task.box = box;
      task.boxProgress = boxProgress;
      task.boxDetail = boxDetail;

      //添加到视图中
      boxView.appendChild(box);

      //---------------- 预览图片并更新UI ----------------
      self.previewImage(boxImage, task, ops).update(task);
    },

    //更新任务界面
    update: function (task) {
      if (!task || !task.box) return;

      var self = this,

        total = task.total || task.size,
        loaded = task.loaded,

        state = task.state,

        boxProgress = task.boxProgress,
        boxDetail = task.boxDetail,

        html_detail = Uploader.getStatusText(state);

      if (self.html5 && loaded != undefined && loaded >= 0) {
        var percentText;

        if (state == Uploader.PROCESSING) {
          var percent = Math.min(loaded * 100 / total, 100);

          percentText = percent.toFixed(1);
          if (percentText == "100.0") percentText = "99.9";

        } else if (state == Uploader.COMPLETE) {
          percentText = "100";
        }

        //进度百分比
        if (percentText) {
          percentText += "%";
          html_detail += " " + percentText;

          boxProgress.style.width = percentText;
        }
      }

      setHtml(boxDetail, html_detail);
    },

    //上传完毕
    over: function (task) {
      if (!task || !task.box) return;

      addClass(task.box, "u-over");
    }
  });

})(window);


;(function () {
  //fileupload
  window.uploader = function (viewName, btnName, url, fnSucc) {
    var Uploader = Q.Uploader,
      formatSize = Q.formatSize,
      boxView = document.querySelector(viewName);

    function getPos(obj) {
      var l = 0;
      var t = 0;
      while (obj) {
        l += obj.offsetLeft;
        t += obj.offsetTop;

        obj = obj.offsetParent;
      }
      return {left: l, top: t};
    }

    var uploader = new Uploader({
      url: url,
      target: document.querySelector(btnName),
      //               auto: false,
      upName: "file",

      // allows: ".jpg,.png,.gif,.bmp",
      clickTrigger: true,
      //图片缩放
      scale: {
        //要缩放的图片格式
        types: ".jpg"
      },
      isQueryState:true,

      workerThread: 2,

      on: {
        //添加之前触发
        add: function (task) {
          if (task.disabled) return hl.toast("允许上传的文件格式为：" + this.ops.allows);
        },
        //图片压缩后触发,如果图片或浏览器不支持压缩,则不触发
        scale: function (data) {
          console.log(data.task.name + " : 已压缩！")
        },
        //图片上传至服务器后触发
        complete: function (responseText) {
          uploader.remove(responseText.id);
          uploader.cancel(responseText.id);
          uploader.remove(responseText.id-1);
          uploader.cancel(responseText.id-1);
          fnSucc(responseText);
        }
      }
    });
  }

  //imgupload
  window.imgupload = function (btnName, url, fnSucc) {
    var Uploader = Q.Uploader,
      formatSize = Q.formatSize;
    // boxView = document.querySelector(viewName);

    function getPos(obj) {
      var l = 0;
      var t = 0;
      while (obj) {
        l += obj.offsetLeft;
        t += obj.offsetTop;

        obj = obj.offsetParent;
      }
      return {left: l, top: t};
    }

		var uploader = new Uploader({
			url: url,
			target: document.querySelector(btnName),
			//               auto: false,
			upName: "file",

			// allows: ".jpg,.png,.jpeg",
      clickTrigger:true,
			//图片缩放
			scale: {
				//要缩放的图片格式
				types: ".jpg",
				//最大图片大小(width|height)
				maxWidth: 1024
			},

      workerThread: 10,

			on: {
				//添加之前触发
				add: function(task) {
					if(task.disabled) return hl.toast("图片格式不正确!请上传格式为JPG、PNG 的图片！");
				},
				//图片压缩后触发,如果图片或浏览器不支持压缩,则不触发
				scale: function(data) {
          console.log(data);

					console.log(data.task.name + " : 已压缩！")
				},
				//图片上传至服务器后触发
        complete:function (responseText) {
            fnSucc(responseText);
        }
      }
    });
  }
  //fileupload
  window.fileUpload = function (btnName, url,fnSucc,progress) {
    var Uploader = Q.Uploader;

    function getPos(obj) {
      var l = 0;
      var t = 0;
      while (obj) {
        l += obj.offsetLeft;
        t += obj.offsetTop;

        obj = obj.offsetParent;
      }
      return {left: l, top: t};
    }

    var uploader = new Uploader({
      url: url,
      target: document.querySelector(btnName),
      upName: "file",
      clickTrigger:true,
      workerThread: 10,
      on: {
        //添加之前触发
        add: function(task) {
          if(task.disabled) return hl.toast("格式不正确!请上传正确的格式！" );
        },
        progress:function(task){
          if(progress && typeof progress == 'function') progress(task)
        },
        complete:function (responseText) {
          if(fnSucc && typeof fnSucc == 'function') fnSucc(responseText)
        }
      },
    });
  }


	//imgupload
	window.imgupload0 = function (btnName, fnSucc) {
	  var Uploader = Q.Uploader,
	    formatSize = Q.formatSize;
	  // boxView = document.querySelector(viewName);

		var uploader = new Uploader({
			url: uploadUrl,
			//               auto: false,
			upName: "file",
			target: document.querySelector(btnName),
			clickTrigger: true,
			// allows: ".jpg,.png,.jpeg",
	    clickTrigger:true,
			//图片缩放
			scale: {
				//要缩放的图片格式
				types: ".jpg",
				//最大图片大小(width|height)
				maxWidth: 1024
			},
	    workerThread: 10,
			on: {
				//添加之前触发
				add: function(task) {
					if(task.disabled) return hl.toast("图片格式不正确!请上传格式为JPG、PNG 的图片！");
				},
				//图片压缩后触发,如果图片或浏览器不支持压缩,则不触发
				scale: function(data) {
					console.log(data.task.name + " : 已压缩！")
				},
				//图片上传至服务器后触发
	      complete:function (responseText) {
	          fnSucc(responseText);
	      }
	    }
	  });
	}
})()
